import { pushTarget, popTarget } from "./dep.js";
import { queueWatcher } from "./scheduler.js";

let id = 0;
export default class Watcher {
  constructor(vm, exprOrFn, cb, options) {
    this.vm = vm;
    this.exprOrFn = exprOrFn;
    this.cb = cb; //回调函数 比如在watcher更新之前可以执行beforeUpdate方法
    this.options = options; //额外的选项 true代表渲染watcher
    this.id = id++; // watcher的唯一标识
    this.deps = []; //存放dep的容器
    this.depsId = new Set(); //用来去重dep

    this.lazy = options.lazy // 标记计算属性watcher
    this.dirty = this.lazy // 标记计算属性是否需要重新计算
    this.user = options.user //标记用户watcher
    // 如果表达式是一个函数
    if (typeof exprOrFn === "function") {
      this.getter = exprOrFn;
    } else {
      // 用户watcher监听,exprOrFn 是监听的属性，可能存在.
      this.getter = function () {
        let path = exprOrFn.split(".");
        let obj = vm;
        for (let i = 0; i < path.length; i++) {
          // 这里读取属性，会触发getter收集依赖
          obj = obj[path[i]];
        }
        return obj;
      };
    }
    this.value = this.lazy ? undefined : this.get(); //记录用户watcher之前的旧值oldValue
  }
  get() {
    pushTarget(this);
    let res = this.getter.call(this.vm); //如果watcher是渲染watcher 那么就相当于执行  vm._update(vm._render()) 这个方法在render函数执行的时候会取值 从而实现依赖收集
    popTarget(); // 在调用方法之后把当前watcher实例从全局Dep.target移除
    return res
  }
  addDep(dep) {
    const id = dep.id;
    if (!this.depsId.has(id)) {
      this.deps.push(dep);
      this.depsId.add(id);
      dep.addSub(this);
    }
  }
  update() {
    // 计算属性依赖的值发生变化 只需要把dirty置为true  下次访问到了重新计算 
    if(this.lazy) {
      this.dirty = true
    }else {
      // 异步执行更新视图：将watcher加入到一个队列中（优化性能，不然每改变一次数据就更新一次视图）
      queueWatcher(this);
    }
    
  }

  depend() {
    // 让渲染函数Watcher收集计算属性中的依赖dep
    let i = this.deps.length
    while(i--) {
      this.deps[i].depend()
    }
  }

  evaluate() {
    this.value = this.get()
    this.dirty = false
  }
  // 真正执行更新的函数
  run() {
    let newValue = this.get();
    let oldValue = this.value
    this.value = newValue
    if(this.user) {
      // 只有数据发生变化时，才触发
      if(newValue !== oldValue || typeof newValue === 'object') {
        // 用户watcher中的hander函数
        this.cb.call(this.vm, newValue, oldValue)
      }
    }else {
      // 渲染watcher
      this.cb && this.cb.call(this.vm)
    }
  }
}
